package org.sipsl.gisystems.gentest;
 
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import org.sipsl.gisystems.gentest.rte.commands.DataCall;
import org.sipsl.gisystems.gentest.rte.commands.DisplayCounter;
import org.sipsl.gisystems.gentest.rte.commands.DisplaySub;
import org.sipsl.gisystems.gentest.rte.commands.ExpectedCounter;
import org.sipsl.gisystems.gentest.rte.commands.ExpectedSub;
import org.sipsl.gisystems.gentest.rte.commands.ModCounter;
import org.sipsl.gisystems.gentest.rte.commands.ResetAccount;
import org.sipsl.gisystems.gentest.rte.commands.ResetCalls;
import org.sipsl.gisystems.gentest.rte.commands.ResetCounter;
import org.sipsl.gisystems.gentest.rte.commands.RteCommand;
import org.sipsl.gisystems.gentest.rte.commands.SetBalance;
import org.sipsl.gisystems.gentest.rte.commands.SetBundle;
import org.sipsl.gisystems.gentest.rte.commands.UnSetBundle;
import org.sipsl.gisystems.gentest.rte.util.BucketLogic;
import org.sipsl.gisystems.gentest.rte.util.BucketLogicData;
import org.sipsl.gisystems.gentest.rte.util.Couple;
import org.sipsl.gisystems.gentest.rte.util.DataParams;
import org.sipsl.gisystems.gentest.rte.util.ExItem;
import org.sipsl.gisystems.gentest.rte.util.GenericUtil;
import org.sipsl.gisystems.gentest.rte.util.RteException;
import org.sipsl.gisystems.rte.V2.TestsuiteV2;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.DataBatchGroup.Zone.DataBatch;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.SubscriberType;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.DataBatchGroup.Datacall;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.DataBatchGroup.Zone.DataBatch.Bundle;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.DataBatchGroup.Zone.DataBatch.DataBundle;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.DataBatchGroup.Zone.DataBatch.DataTariff;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.VoiceBatchGroup.Zone.VoiceBatch;
import org.sipsl.gisystems.rte.V2.TestsuiteV2.VoiceBatchGroup.Zone.VoiceBatch.VoiceBundle;

import com.rits.cloning.Cloner;

public class RteDataTestUnit {

	// test

	private DataParams dataParams;
	private ArrayList<RteCommand> testInstructions;
	private long amountOfDataToCall;
	private long amountToSpend;

	public RteDataTestUnit(TestsuiteV2 bix, TestsuiteV2 surepay,
			DataParams dataParams2, int index, List<DataTariff> charge,
			int chargeIndex, DataBatch dtb, List<Datacall> datacall,
			SubscriberType subscriberType, Cloner cloner, boolean singleTest, ArrayList<String> eusgsn) {

		try {

			dataParams = dataParams2;

			int ShowMe = 0;

			testInstructions = new ArrayList<RteCommand>();

			int SubType = 0;

			// Reset account and counter if asked
			if (subscriberType != null) {
				SubType = (subscriberType.getType().compareTo("PostPaid") == 0) ? 1
						: 0;
				if (subscriberType.getResetAccount() != null) {
					testInstructions.add(new ResetAccount(dataParams,
							subscriberType.getResetAccount().getTemplate()));
				}
				if (subscriberType.getResetCounter() != null) {
					for (org.sipsl.gisystems.rte.V2.TestsuiteV2.SubscriberType.ResetCounter s : subscriberType
							.getResetCounter()) {
						testInstructions.add(new ResetCounter(dataParams, s
								.getTemplate()));
					}
				}
			}

			testInstructions.add(new ResetCalls(dataParams));
			testInstructions.add(new SetBalance(SubType, dataParams));

			if (dtb.getNewBundleLogic() == null
					|| (dtb.getNewBundleLogic() != null && dtb
							.getNewBundleLogic().toUpperCase().compareTo("YES") == 0)) {

				ArrayList<DataBundle> Bundles = new ArrayList<DataBundle>();

				Queue<BucketLogicData> BucketQueue = new LinkedList<BucketLogicData>();

				for (DataBundle bv2 : dtb.getDataBundle()) {

					// load from surepay.xml if referenced
					// apply is copied from the scenario
					// TODO need to copy also the positions
					if (bv2.getReference() != null) {
						DataBundle tbd;
						tbd = getBundleFromSurepay(surepay, bv2.getReference(),
								cloner);
						if (bv2.getApply() == null) {

						} else {
							tbd.setApply(bv2.getApply().toUpperCase());

							String position[] = new String[3];
							position = bv2.getPositions().split(",");
							
							ExItem<String[]> counterIds = new ExItem<String[]>();
							if (bv2.getCounterids()!=null){
								String[] s = new String[3];
								s = bv2.getCounterids().split(",");
								counterIds.setValue(s);
							}

							if (bv2.getStartDate() != null) {
								tbd.setStartDate(bv2.getStartDate());
							}
							if (bv2.getEndDate() != null) {
								tbd.setEndDate(bv2.getEndDate());
							}
							if (bv2.getStartTime() != null) {
								tbd.setStartTime(bv2.getStartTime());
							}
							if (bv2.getEndTime() != null) {
								tbd.setEndTime(bv2.getEndTime());
							}

							if (tbd.getDataBundleBucket() != null) {
								tbd.getDataBundleBucket().setPosition(
										new Integer(position[0]));
								if (counterIds.isValid() && counterIds.getValue()[0].length()>0 ){
									tbd.getDataBundleBucket().setCounterid(counterIds.getValue()[0]);
								}
							}
							if (tbd.getDataBundleCounter() != null) {
								tbd.getDataBundleCounter().setPosition(
										new Integer(position[1]));
								if (counterIds.isValid() && counterIds.getValue()[1].length()>0){
									tbd.getDataBundleCounter().setCounterid(counterIds.getValue()[1]);
								}
							}
							if (tbd.getDataBundleUBD() != null
									&& position[2] != null) {
								tbd.getDataBundleUBD().setPosition(
										new Integer(position[2]));
								if (counterIds.isValid() && counterIds.getValue()[2].length()>0){
									tbd.getDataBundleUBD().setCounterid(counterIds.getValue()[2]);
								}

							}
						}
						Bundles.add(tbd);

					} else {
						Bundles.add(bv2);
					}
				}

				// ExItem<Long> _bundleLimit = new ExItem<Long>();

				// int counterPosition = -1;
				ExItem<Integer> _counterPosition = new ExItem<Integer>();
				ExItem<String> _counterUnit = new ExItem<String>();
				ExItem<String> _counterIdCounter = new ExItem<String>();
				ExItem<Integer> _discountPosition = new ExItem<Integer>();
				ExItem<Double> _UBDcap = new ExItem<Double>();
				ExItem<String> _UBDcapunit = new ExItem<String>();
				ExItem<String> _counterIdUBD = new ExItem<String>();
				ExItem<Double> _spendingCap = new ExItem<Double>();

				for (DataBundle bundle : Bundles) {
					testInstructions.add(new SetBundle(dataParams, bundle
							.getId(), GenericUtil.parseDate(GenericUtil
							.nullToEmpty(bundle.getStartDate())), GenericUtil
							.parseDate(GenericUtil.nullToEmpty(bundle
									.getEndDate())), GenericUtil
							.parseTime(GenericUtil.nullToEmpty(bundle
									.getStartTime())), GenericUtil
							.parseTime(GenericUtil.nullToEmpty(bundle
									.getEndTime()))));

					// get limit if exists
					// if (bundle.getDataBundleLimit() != null) {
					// _bundleLimit.setValue(GenericUtil.getBytes(bundle
					// .getDataBundleLimit().getSize()));
					// }

					// get bucket if exists
					if (bundle.getDataBundleBucket() != null) {

						// boolean isGiga =
						// bundle.getDataBundleBucket().getSize().endsWith("G")
						// ||
						// GenericUtil.getBytes(bundle.getDataBundleBucket().getSize(),
						// false) >= GenericUtil.getBytes("1G",false) ;
						String unit = "B";
						if (bundle.getDataBundleBucket().getUnit() != null) {
							unit = bundle.getDataBundleBucket().getUnit();
						}
						ExItem<String> _counterId = new ExItem<String>();

						if (bundle.getDataBundleBucket().getCounterid() !=null){
							String s = new String(bundle.getDataBundleBucket().getCounterid());
							_counterId.setValue(s);
						}
						BucketQueue.add(new BucketLogicData(GenericUtil
								.getBytes(bundle.getDataBundleBucket()
										.getSize()), 0, unit, bundle.getApply()
								.toUpperCase().compareTo("YES") == 0, bundle
								.getDataBundleBucket().getPosition(), bundle, _counterId));

					}
					// get counter if exists
					if (bundle.getDataBundleCounter() != null) {
						if (bundle.getDataBundleCounter().getCounterid()!=null){
							String s = new String();
							s = bundle.getDataBundleCounter().getCounterid();
							_counterIdCounter.setValue(s);
						}
						if (bundle.getApply().toUpperCase().compareTo("YES") == 0) {
							_counterPosition.setValue(bundle
									.getDataBundleCounter().getPosition());
							if (bundle.getDataBundleCounter().getUnit() != null) {
								_counterUnit.setValue(bundle
										.getDataBundleCounter().getUnit());
							}
						} else if (bundle.getApply().toUpperCase()
								.compareTo("YES") != 0) {
							_counterPosition.setValue(bundle
									.getDataBundleCounter().getPosition());
							_counterPosition.unSet2();
						}
					}

					if (bundle.getDataBundleUBD() != null) {
						if (bundle.getDataBundleUBD().getCounterid()!=null){
							String s = new String();
							s = bundle.getDataBundleUBD().getCounterid();
							_counterIdUBD.setValue(s);
						}
						if (bundle.getApply().toUpperCase().compareTo("YES") == 0) {
							_discountPosition.setValue(bundle
									.getDataBundleUBD().getPosition());
							


							if (bundle.getDataBundleUBD().getCap() != null) {
								// _UBDcap.setValue(GenericUtil.getBytes(bundle.getDataBundleUBD().getCap()));
								_UBDcap.setValue(bundle.getDataBundleUBD()
										.getCap().doubleValue());
								if (bundle.getDataBundleUBD().getUnit() != null) {
									_UBDcapunit.setValue(bundle
											.getDataBundleUBD().getUnit());
								}
							}

						} else if (bundle.getApply().toUpperCase()
								.compareTo("YES") != 0) {
							_discountPosition.setValue(bundle
									.getDataBundleUBD().getPosition());
							_discountPosition.unSet2();
						}
					}
				}

				// Make call
				// amountToCall is always in bytes
				long amountToCallInBytes = GenericUtil.getBytes(datacall.get(
						index - 4).getSize());

				if (singleTest){
					long singleTestAmountToCallInBytes = 0;
					for (String sgsn : eusgsn){
						String s[] = new String[2];
						s = sgsn.split("#");
						GenericUtil.LOG("sgsn " + s[1],ShowMe,0);
						dataParams.setSGSN(s[1]);
						
						testInstructions.add(new DataCall(dataParams,
								amountToCallInBytes));
						
						singleTestAmountToCallInBytes = singleTestAmountToCallInBytes + amountToCallInBytes;

					}
					amountToCallInBytes = singleTestAmountToCallInBytes;
				}else{
					testInstructions.add(new DataCall(dataParams,
							amountToCallInBytes));
				}

				// display
				testInstructions.add(new DisplaySub(dataParams));
				if (GenTest.enqRteOption(bix, "USE_DISPLAY_COUNTER1")) {
					testInstructions.add(new DisplayCounter(1, dataParams));
				}
				if (GenTest.enqRteOption(bix, "USE_DISPLAY_COUNTER2")) {
					testInstructions.add(new DisplayCounter(2, dataParams));
				}

				// calculate used bucket
				ExItem<Long> _excessToPay = new ExItem<Long>();

				Map<DataBundle, Boolean> IsEmpty = new HashMap<DataBundle, Boolean>();

				// Allowances
				while (!BucketQueue.isEmpty()) {
					BucketLogicData bulo = BucketQueue.remove();
					if (bulo.getCounterId().isValid()){
						dataParams.getParamMap().put("COUNTERIDCHECKVALUE", bulo.getCounterId().getValue());
					}

					if (bulo.isApply()) {
						// up the amount to the same unit
						long localAmount = GenericUtil.roundUp(
								amountToCallInBytes, bulo.getUnit());
						// use all bucket
						if (localAmount >= bulo.getBucketSize()) {
							bulo.setBucketLeft(0);
							localAmount = localAmount - bulo.getBucketSize();
							bulo.setBucketLeft(0);

							testInstructions.add(new ExpectedCounter(bulo
									.getBucketLeft(), bulo.getPosition(),
									dataParams));
							// back in bytes
							amountToCallInBytes = localAmount
									* (long) Math.pow(1024, GenericUtil
											.getUnitPow(bulo.getUnit()));

							IsEmpty.put(bulo.getDataBundle(), new Boolean(true));
						}
						// amountToCall < bucket
						else {
							bulo.setBucketLeft(bulo.getBucketSize()
									- localAmount);
							localAmount = 0;
							amountToCallInBytes = 0;
							testInstructions.add(new ExpectedCounter(bulo
									.getBucketLeft(), bulo.getPosition(),
									dataParams));
							IsEmpty.put(bulo.getDataBundle(),
									new Boolean(false));
						}

					} else {// apply - no
						testInstructions.add(new ExpectedCounter(bulo
								.getBucketSize(), bulo.getPosition(),
								dataParams));
					}
					dataParams.getParamMap().remove("COUNTERIDCHECKVALUE");
				}
				// CAP
				// The data call excess of allowances shall be capped if there
				// is a cap
				if (_discountPosition.isValid() && _UBDcap.isValid()) {
					if (_UBDcapunit.isValid()
							&& _UBDcapunit.getValue().compareTo("data") == 0) {
						// if (amountToCallInBytes > _UBDcap.getValue()){
						// amountToCallInBytes = _UBDcap.getValue();
						// }
					} else {
						_spendingCap.setValue(_UBDcap.getValue());
					}

				}
				_excessToPay.setValue(amountToCallInBytes);
				// calculate payment
				ExItem<Long> _resultToPay = new ExItem<Long>();
				// check if predefined speding
				for (TestsuiteV2.DataBatchGroup.Datacall.Result r : datacall
						.get(index - 4).getResult()) {
					if (r.getName().compareTo(dtb.getId()) == 0) {
						if (r.getValue() != null) {
							_resultToPay.setValue(r.getValue().longValue());
							break;
						}
					}
				}

				Couple<Long, Long> spent = new Couple<Long, Long>();
				ExItem<Long> first = new ExItem<Long>();
				ExItem<Long> second = new ExItem<Long>();
				spent.setFirst(first);
				spent.setSecond(second);

				if (!_resultToPay.isValid()) {
					spent = getSpending(charge, _excessToPay.getValue(),
							_spendingCap);
				} else if (_resultToPay.isValid()) {
					spent.getFirst().setValue(_resultToPay.getValue());
				}

				//////////////////////////////////////////////////////////////////////////////
				// THIS IS AFFECTED BY CAP!!!
				if (_counterPosition.isValid() && _counterIdCounter.isValid()){
					dataParams.getParamMap().put("COUNTERIDCHECKVALUE", _counterIdCounter.getValue());
				}
				if (_counterPosition.isValid() && _counterPosition.isValid2()) {//APPLY YES
					
					
					long amountToCallUnit;
					if (spent.getSecond().isValid()
							&& spent.getSecond().getValue() < amountToCallInBytes) {
						GenericUtil.LOG("CAPPED "
								+ spent.getSecond().getValue(), ShowMe, 0);
						amountToCallInBytes = spent.getSecond().getValue();
					}
					if (_counterUnit.isValid()) {
						amountToCallUnit = GenericUtil.roundUp(
								amountToCallInBytes, _counterUnit.getValue());
					} else {
						amountToCallUnit = amountToCallInBytes;
					}
					testInstructions.add(new ExpectedCounter(amountToCallUnit,
							_counterPosition.getValue(), dataParams));

				} else if (_counterPosition.isValid() //APPLY = NO
						&& !_counterPosition.isValid2()) {
					testInstructions.add(new ExpectedCounter(0,
							_counterPosition.getValue(), dataParams));
				}
				if (_counterPosition.isValid() && _counterIdCounter.isValid()){
					dataParams.getParamMap().remove("COUNTERIDCHECKVALUE");
				}
				//////////////////////////////////////////////////////////////////////////////

				
				
		
				//////////////////////////////////////////////////////////////////////////////
				if (_discountPosition.isValid() && _counterIdUBD.isValid()){
					dataParams.getParamMap().put("COUNTERIDCHECKVALUE", _counterIdUBD.getValue());
				}
				if (_discountPosition.isValid() && _discountPosition.isValid2()) {//APPLY YES

					testInstructions.add(new ExpectedCounter(spent.getFirst()
							.getValue().intValue(), _discountPosition
							.getValue(), dataParams));
					
					
				} else if (_discountPosition.isValid()
						&& !_discountPosition.isValid2()) { //APPLY NO
					testInstructions.add(new ExpectedCounter(0,
							_discountPosition.getValue(), dataParams));
				}
				if (_discountPosition.isValid() && _counterIdUBD.isValid()){
					dataParams.getParamMap().remove("COUNTERIDCHECKVALUE");
				}
				//////////////////////////////////////////////////////////////////////////////

				
				
				if (SubType == 0) {
					int bal = (int) (Integer.parseInt(dataParams.getBALANCE()
							.replace(".", "") + "000") - spent.getFirst()
							.getValue());
					testInstructions.add((new ExpectedSub(bal, SubType,
							dataParams)));
				} else if (SubType == 1) {
					testInstructions.add((new ExpectedSub(spent.getFirst()
							.getValue().intValue(), SubType, dataParams)));
				}

				for (DataBundle bundle : Bundles) {
					if (bundle.getApply() != null
							&& bundle.getApply().compareTo("YES") == 0) {
						if (bundle.getDeleteOnExhaust() != null
								&& bundle.getDeleteOnExhaust().toUpperCase()
										.compareTo("YES") == 0
								&& IsEmpty.get(bundle)) {
							// skip deletion
						} else {
							testInstructions.add(new UnSetBundle(dataParams,
									bundle.getId()));
						}
					} else {
						testInstructions.add(new UnSetBundle(dataParams, bundle
								.getId()));
					}
				}
			}

			if (GenTest.enqRteOption(bix, "USE_MODIFY_COUNTER")) {
				testInstructions.add(new ModCounter(dataParams));
			}
		} catch (RteException e) {
			e.printStackTrace();
			System.exit(0);
		}

	}

	// Test Doc
	// id 1
	// Subject Data Traveller
	// Test Name Data Call for 1000 bytes from EU, SGSN TUR4
	// Test Objective Verify that the call is charged or the bundle X is used
	// and the bundle Y is not used
	// Step Name subscribe to bundle...
	// Description msisdn = xxx, .....

	// private DataBundle getBundleFromSurepay(TestsuiteV2 surepay,
	// String reference) {
	//
	// for (DataBatch db :
	// surepay.getDataBatchGroup().getZone().get(0).getDataBatch()){
	// if (db.getDataBundle().size() == 0){
	// continue;
	// }
	// if (db.getDataBundle().get(0).getReference().compareTo(reference) == 0){
	// return db.getDataBundle().get(0);
	// }
	// }
	// return null;
	// }

	private DataBundle getBundleFromSurepay(TestsuiteV2 surepay,
			String reference, Cloner cloner) {

		for (DataBatch db : surepay.getDataBatchGroup().getZone().get(0)
				.getDataBatch()) {
			if (db.getDataBundle().size() == 0) {
				continue;
			}
			if (db.getDataBundle().get(0).getReference().compareTo(reference) == 0) {
				DataBundle vbd = (DataBundle) cloner.deepClone(db
						.getDataBundle().get(0));
				return vbd;
			}
		}
		return null;
	}

	public void generateDocData(int testNumber, String testSuiteName,
			String testName, String callingZone, boolean isNew) {
		// //BRS ID : test number
		// //Subject : Topic of test : <testsuiteV2 id="TestVoiceV3">
		// //Test Name : Name of test : testName
		// //Test Objective :Verify that... call costs...
		// //Step Name (Design Steps) : subscribe to, make a call from a to b of
		// tot secs
		// //Description (Design Steps) things to verify (ama etc...)

		int ShowMe = 0;

		testNumber++;

		String TEST = new String();

		GenericUtil.LOG("BRS ID " + testNumber, ShowMe, 0);
		TEST = "" + testNumber + ",";
		GenericUtil.LOG("Subject " + testSuiteName, ShowMe, 0);
		TEST = TEST + testSuiteName + ",";
		GenericUtil.LOG("Test Name " + testName, ShowMe, 0);
		TEST = TEST + testName + ",";

		// ////

		TEST = TEST + "Originating Zone " + callingZone
				+ generateTestDocument();
		FileWriter fstream;
		try {
			fstream = new FileWriter(testSuiteName + ".csv", isNew);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(TEST + "\n");
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void generateDocData2(int testNumber, String subject, String Zone,
			String apn, String sgsn, List<Bundle> bundleList, DataParams params) {

		GenericUtil.LOG("TEST DOC", 0, 0);
		GenericUtil.LOG("Test Number   " + testNumber, 0, 0);
		GenericUtil.LOG("Test Subject  " + subject, 0, 0);
		GenericUtil.LOG("Test Zone     " + Zone, 0, 0);
		String toSpendPhrase;
		String TestDescription;
		String TestName;
		String TestSteps = "";
		if (amountToSpend == 0) {
			toSpendPhrase = " is free of charge ";
		} else {
			toSpendPhrase = " is charged " + amountToSpend + " units ";
		}
		if (sgsn != null) {
			TestName = "Data Call from " + sgsn + " (" + Zone + ") of "
					+ amountOfDataToCall + " bytes using " + apn;
			TestDescription = "Verify that a Data Call from " + sgsn + " ("
					+ Zone + ") using " + apn + " of " + amountOfDataToCall
					+ " bytes" + toSpendPhrase;
		} else {
			TestName = "Data Call from " + Zone + " of " + amountOfDataToCall
					+ " bytes using " + apn;
			TestDescription = "Verify that a Data Call from " + Zone
					+ " using " + apn + " of " + amountOfDataToCall + " bytes"
					+ toSpendPhrase;
		}
		if (bundleList != null) {
			for (Bundle b : bundleList) {
				if (b.getApply() != null && b.getApply().compareTo("yes") == 0) {
					TestDescription = TestDescription + "and the bundle "
							+ b.getId() + " is used ";
				} else if (b.getApply() != null
						&& b.getApply().compareTo("no") == 0) {
					TestDescription = TestDescription + "and the bundle "
							+ b.getId() + " is not used ";
				}
			}
		}
		GenericUtil.LOG(TestName, 0, 0);
		GenericUtil.LOG(TestDescription, 0, 0);

		if (bundleList != null && !bundleList.isEmpty()) {
			TestSteps = "Subscribe to the following bundle(s): ";
			for (Bundle b : bundleList) {
				TestSteps = TestSteps + "" + b.getId() + " ";
			}
		}
		TestSteps = TestSteps + "Using Rte make a call of "
				+ amountOfDataToCall + " subscriber: " + params.getMSISDN()
				+ " category: " + params.getCATEGORY_ID() + " RuleBaseId: "
				+ params.getRULEBASE_ID();

		GenericUtil.LOG(TestSteps, 0, 0);

		FileWriter fstream;
		try {
			fstream = new FileWriter(subject + ".csv", true);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(testNumber + "," + subject + "," + TestName + ","
					+ TestDescription + "," + TestSteps + "\n");
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private Couple<Long, Long> getSpending(List<DataTariff> cl, long amount,
			ExItem<Double> cap) {

		long tmp = 0;
		long tmp2 = 0;

		int ShowMe = 0;
		GenericUtil.LOG("Start Charges " + cl.size(), ShowMe, 0);
		ExItem<Long> capInUnitsVar = new ExItem<Long>();
		ExItem<Long> capInUnits = new ExItem<Long>();
		Couple<Long, Long> result = new Couple<Long, Long>();
		ExItem<Long> first = new ExItem<Long>();
		ExItem<Long> second = new ExItem<Long>();

		try {
			first.setValue((long) 0);
			// second.setValue((long)0);

			result.setFirst(first);
			result.setSecond(second);

			if (cap.isValid()) {
				Double app = new Double(cap.getValue() * 100000);
				capInUnitsVar.setValue(app.longValue());
				if (capInUnitsVar.getValue() < 0){
					GenericUtil.LOG("609 negativo",ShowMe,0);
				}
				capInUnits.setValue(app.longValue());
			}

			for (DataTariff cc : cl) {
				GenericUtil.PICO(595, ShowMe);

				if (cc.getOutOfBundleFee() != null) {

					GenericUtil.PICO(598, ShowMe);
					long s = (long) cc.getOutOfBundleFee().doubleValue() * 100000;

					tmp = result.getFirst().getValue();
					result.getFirst().setValue(tmp + s);

					if (capInUnitsVar.isValid()) {
						capInUnitsVar.setValue(capInUnitsVar.getValue() - s);
						if (capInUnitsVar.getValue() < 0){
							GenericUtil.LOG("628 negativo",ShowMe,0);
							throw new RteException("capInUnitsVar negative " +capInUnitsVar.getValue());
						}
						if (!result.getSecond().isValid()) {
							result.getSecond().setValue((long) 0);
						}
						tmp2 = result.getSecond().getValue();
						result.getSecond().setValue(tmp2);
					}
					GenericUtil.LOG(
							"getOutOfBundleFee "
									+ cc.getOutOfBundleFee().doubleValue()
									* 1000 * 100, ShowMe, 0);
				}

				if (amount != 0
						&& cc.getTo() != null
						&& amount <= (GenericUtil.getBytes(cc.getTo()) - GenericUtil
								.getBytes(cc.getFrom()))) {
					GenericUtil.PICO(612, ShowMe);
					if ((cc.getPrice()).floatValue() != 0) {
						Couple<Long, Long> s = calculateMe(amount,
								GenericUtil.getBytes(cc.getChunk()), cc
										.getPrice().doubleValue(),
								capInUnitsVar);
						if (capInUnitsVar.isValid() ){
							capInUnitsVar.setValue((long) (capInUnitsVar.getValue() - (s.getFirst().getValue() )));

							if (capInUnitsVar.getValue() < 0){
								GenericUtil.LOG("663 negativo",ShowMe,0);
								throw new RteException("capInUnitsVar negative " +capInUnitsVar.getValue());
							}

							if (!result.getSecond().isValid()) {
								result.getSecond().setValue((long) 0);
							}
							tmp2 = result.getSecond().getValue();
							result.getSecond().setValue(
									tmp2 + s.getSecond().getValue());
						}
						tmp = result.getFirst().getValue();
						result.getFirst().setValue(
								tmp + s.getFirst().getValue());
						if (!s.getFirst().isValid2()){
							// over cap
							break;
						}
					}
					amount = 0;
				} else if (amount != 0
						&& cc.getTo() != null
						&& amount > GenericUtil.getBytes(cc.getTo())
								- GenericUtil.getBytes(cc.getFrom())) {
					GenericUtil.PICO(636, ShowMe);
					if ((cc.getPrice()).floatValue() != 0) {
						Couple<Long, Long> s = calculateMe(
								GenericUtil.getBytes(cc.getTo())
										- GenericUtil.getBytes(cc.getFrom()),
								GenericUtil.getBytes(cc.getChunk()), cc
										.getPrice().doubleValue(),
								capInUnitsVar);
						if (capInUnitsVar.isValid()){
							GenericUtil.PICO(647, ShowMe);
							capInUnitsVar.setValue((long) (capInUnitsVar.getValue() - (s.getFirst().getValue() )));

							if (capInUnitsVar.getValue() < 0){
								throw new RteException("capInUnitsVar negative " +capInUnitsVar.getValue());
							}

							if (!result.getSecond().isValid()) {
								result.getSecond().setValue((long) 0);
							}
							tmp2 = result.getSecond().getValue();
							result.getSecond().setValue(
									tmp2 + s.getSecond().getValue());
						}

						tmp = result.getFirst().getValue();
						result.getFirst().setValue(
								tmp + s.getFirst().getValue());
						if (!s.getFirst().isValid2()){
							// over cap
							break;
						}
					}
					amount = amount
							- (GenericUtil.getBytes(cc.getTo()) - GenericUtil
									.getBytes(cc.getFrom()));
				} else if (amount != 0 && cc.getTo() == null) {
					GenericUtil.PICO(664, ShowMe);
					if ((cc.getPrice()).floatValue() != 0) {
						Couple<Long, Long> s = calculateMe(amount,
								GenericUtil.getBytes(cc.getChunk()), cc
										.getPrice().doubleValue(),
								capInUnitsVar);
						if (capInUnitsVar.isValid() ) {
							capInUnitsVar.setValue((long) (capInUnitsVar.getValue() - (s.getFirst().getValue() )));

							if (capInUnitsVar.getValue() < 0){
								throw new RteException("capInUnitsVar negative " +capInUnitsVar.getValue());
							}
							if (!result.getSecond().isValid()) {
								result.getSecond().setValue((long) 0);
							}
							tmp2 = result.getSecond().getValue();
							result.getSecond().setValue(
									tmp2 + s.getSecond().getValue());
						}
						tmp = result.getFirst().getValue();
						result.getFirst().setValue(
								tmp + s.getFirst().getValue());
						if (!s.getFirst().isValid2()){
							// over cap
							break;
						}
					}
					amount = 0;
				} else {
					break;
				}
				GenericUtil.LOG(
						"Iter From [" + cc.getFrom() + "] To [" + cc.getTo()
								+ "]", ShowMe, 0);
				GenericUtil.LOG("Iter Amount [" + amount + "] Spending ["
						+ result.getFirst().getValue() + "]", ShowMe, 0);

			}
			GenericUtil.LOG("End Amount [" + amount + "] Spending ["
					+ result.getFirst().getValue() + "]", ShowMe, 0);

		} catch (RteException e) {
			e.printStackTrace();
			System.exit(0);
		}

		// try {
		// GenericUtil.LOG("result first " +
		// result.getFirst().getValue(),ShowMe,0);
		// GenericUtil.LOG("result second " +
		// result.getSecond().getValue(),ShowMe,0);
		//
		// }catch (RteException e) {
		// e.printStackTrace();
		// System.exit(0);
		// }
		return result;
	}

	public static Couple<Long, Long> calculateMe(long amount, long chunk,
			double price, ExItem<Long> remainingCapInUnits) {

		int ShowMe = 0;
		GenericUtil.LOG("calculateMe", ShowMe, 0);
		GenericUtil.LOG("amount " + amount + " chunk " + chunk + " price " + price, ShowMe, 0);

		long newAmount;
		if (amount < chunk) {
			newAmount = chunk;
		} else {
			newAmount = amount;
		}
		
		boolean underCap = true;

		double a = (double) newAmount;
		double b = a / chunk;
		double numOfChunks = Math.ceil(b);

		if (remainingCapInUnits.isValid()) {// how many chunks in the cap
			try {
				GenericUtil.LOG("remainingCapInUnits.getValue() " + remainingCapInUnits.getValue(), ShowMe, 0);

				double b_cap = remainingCapInUnits.getValue()
						/ (price * 100000);
				GenericUtil.LOG("b_cap " + b_cap, ShowMe, 0);

				int chunksInCap = (int) Math.floor(b_cap);
				GenericUtil.LOG("chunksInCap " + chunksInCap, ShowMe, 0);

				if (chunksInCap >= numOfChunks) {
					// under cap
				} else {
					numOfChunks = chunksInCap;
					///Over cap
					underCap = false;
				}
			} catch (RteException e) {
				e.printStackTrace();
				System.exit(0);
			}

		}

		double pricePerChunk = price * 100000;

		double dd = numOfChunks * pricePerChunk;

		double e = Math.ceil(dd);
		Long g = (long) e;

		GenericUtil.LOG("a " + a, ShowMe, 0);
		GenericUtil.LOG("b " + b, ShowMe, 0);
		GenericUtil.LOG("unitsPerChunk " + numOfChunks, ShowMe, 0);

		GenericUtil.LOG("pricePerChunk " + pricePerChunk, ShowMe, 0);
		GenericUtil.LOG("dd " + dd, ShowMe, 0);

		GenericUtil.LOG("e " + e, ShowMe, 0);
		GenericUtil.LOG("pay " + g, ShowMe, 0);
		GenericUtil.LOG("consumed " + (long) (numOfChunks * chunk), ShowMe, 0);

		Couple<Long, Long> result = new Couple<Long, Long>();
		ExItem<Long> first = new ExItem<Long>();
		first.setValue(g);
		if (!underCap){
			first.unSet2();
		}
		ExItem<Long> second = new ExItem<Long>();
		second.setValue((long) (numOfChunks * chunk));

		//speding in units
		result.setFirst(first);
		//how much used in b
		result.setSecond(second);

		return result;

	}

	public String generateTestDocument() {
		int i = 0;
		String test = "";
		for (RteCommand s : testInstructions) {
			if (s.getTestDescription().length() > 0)
				test = test + "\n" + s.getTestDescription();
		}
		return test;
	}

	public void generateTest(String title) {
		int i = 0;
		(new File(title)).mkdir();
		for (RteCommand s : testInstructions) {
			s.dumpScript(title, i++);
		}
	}
}
